﻿// <copyright file="Gameboard.cs" company="ALCPU">
// Copyright (c) 2010 All Right Reserved
// </copyright>
// <author>Arthur Liberman</author>
// <email>Arthur_Liberman@hotmail.com</email>
// <date>03-31-2010</date>
// <summary>Gameboard code.</summary>

namespace Chess
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using Chess.Figures;
    using ChessDefinitions;

    /// <summary>
    /// Gameboard class
    /// </summary>
    public class Gameboard
    {
        /// <summary>
        /// The array of pieces on the gameboard.
        /// </summary>
        private IPiece[,] pieces;

        /// <summary>
        /// The array of squares on the gameboard.
        /// </summary>
        private Square[,] squares;

        /// <summary>
        /// A reference to the grid.
        /// </summary>
        private Grid gameGrid;

        /// <summary>
        /// Initializes a new instance of the <see cref="Gameboard"/> class.
        /// </summary>
        /// <param name="gameboardGrid">The gameboard grid.</param>
        public Gameboard(Grid gameboardGrid)
        {
            Square square;
            this.squares = new Square[8, 8];
            this.pieces = new IPiece[8, 8];
            this.Squares = new DoubleIndexedProperty<Square>(this.squares);
            this.Figures = new DoubleIndexedProperty<IPiece>(this.pieces);
            this.gameGrid = gameboardGrid;
            gameboardGrid.Children.Clear();
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    square = new Square();
                    this.squares[i, j] = square;
                    this.pieces[i, j] = null;
                    this.squares[i, j].Image = null;
                    Grid.SetColumn(this.squares[i, j], i);
                    Grid.SetRow(this.squares[i, j], j);
                    gameboardGrid.Children.Add(this.squares[i, j]);
                }
            }

            this.RecolorGameboard();
        }

        /// <summary>
        /// Gets or sets the pieces.
        /// </summary>
        /// <value>The pieces.</value>
        public DoubleIndexedProperty<IPiece> Figures { get; protected set; }

        /// <summary>
        /// Gets or sets the squares.
        /// </summary>
        /// <value>The squares.</value>
        public DoubleIndexedProperty<Square> Squares { get; protected set; }

        /// <summary>
        /// Recolors the gameboard.
        /// </summary>
        public void RecolorGameboard()
        {
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    if (this.squares[i, j].DisplayMode != SquareDisplayMode.Normal)
                    {
                        this.squares[i, j].ShowNormal();
                    }
                }
            }
        }

        /// <summary>
        /// Clears the square.
        /// </summary>
        /// <param name="target">The target.</param>
        public void ClearSquare(Position target)
        {
            if (target.Y >= 0 && target.X >= 0 && target.X < 8 && target.Y < 8)
            {
                this.squares[target.X, target.Y].Image = null;
                this.pieces[target.X, target.Y] = null;
            }
        }

        /// <summary>
        /// Determines whether the square specified by target is free.
        /// </summary>
        /// <param name="target">The target.</param>
        /// <returns>
        ///     <c>true</c> if the square is free; otherwise, <c>false</c>.
        /// </returns>
        public bool IsSquareFree(Position target)
        {
            bool ret = false;

            if (target.Y >= 0 && target.X >= 0 && target.X < 8 && target.Y < 8 &&
                this.pieces[target.X, target.Y] == null)
            {
                ret = true;
            }

            return ret;
        }

        /// <summary>
        /// Determines whether the is square attackable by the specified target.
        /// </summary>
        /// <param name="target">The target.</param>
        /// <param name="piece">The piece.</param>
        /// <returns>
        ///     <c>true</c> if square is attackable; otherwise, <c>false</c>.
        /// </returns>
        public bool IsSquareAttackable(Position target, IPiece piece)
        {
            bool ret = false;

            if (target.Y >= 0 && target.X >= 0 && target.X < 8 && target.Y < 8 &&
                piece != null && this.pieces[target.X, target.Y] != null)
            {
                if (this.pieces[target.X, target.Y].Color != piece.Color)
                {
                    ret = true;
                }
            }

            return ret;
        }

        /// <summary>
        /// Highlights the available square.
        /// </summary>
        /// <param name="target">The target square.</param>
        public void ShowAvailable(Position target)
        {
            if (target.X >= 0 && target.X < 8 && target.Y >= 0 && target.Y < 8)
            {
                this.squares[target.X, target.Y].ShowAvailable();
            }
        }

        /// <summary>
        /// Highlights the available squares around the <paramref name="piece"/>.
        /// </summary>
        /// <param name="piece">The piece to be checked.</param>
        public void ShowAvailable(IPiece piece)
        {
            if (piece != null)
            {
                foreach (Position pos in piece.MoveOptions)
                {
                    this.ShowAvailable(pos);
                }
            }
        }

        /// <summary>
        /// Highlights the attackable square.
        /// </summary>
        /// <param name="target">The target square.</param>
        public void ShowAttackable(Position target)
        {
            if (target.X >= 0 && target.X < 8 && target.Y >= 0 && target.Y < 8)
            {
                this.squares[target.X, target.Y].ShowAttackable();
            }
        }

        /// <summary>
        /// Highlights the attackable squares around the <paramref name="piece"/>.
        /// </summary>
        /// <param name="piece">The piece to be checked.</param>
        public void ShowAttackable(IPiece piece)
        {
            if (piece != null)
            {
                foreach (Position pos in piece.AttackOptions)
                {
                    this.ShowAttackable(pos);
                }
            }
        }

        /// <summary>
        /// Highlights the selected square.
        /// </summary>
        /// <param name="target">The target square.</param>
        public void ShowSelected(Position target)
        {
            if (target.X >= 0 && target.X < 8 && target.Y >= 0 && target.Y < 8)
            {
                this.squares[target.X, target.Y].ShowSelected();
            }
        }

        /// <summary>
        /// Shows the normal square color.
        /// </summary>
        /// <param name="target">The target square.</param>
        public void ShowNormal(Position target)
        {
            if (target.X >= 0 && target.X < 8 && target.Y >= 0 && target.Y < 8)
            {
                this.squares[target.X, target.Y].ShowNormal();
            }
        }

        /// <summary>
        /// Moves the piece to the <paramref name="target" /> square.
        /// </summary>
        /// <param name="target">The target square.</param>
        /// <param name="piece">The piece to be moved.</param>
        /// <returns><c>true</c> if successfully moved; otherwise, <c>false</c>.</returns>
        public bool MovePiece(Position target, IPiece piece)
        {
            bool ret = false;

            if (piece != null && target.X >= 0 && target.X < 8 && target.Y >= 0 && target.Y < 8 &&
                piece is PieceBase)
            {
                this.squares[piece.Position.X, piece.Position.Y].Image = null;
                this.pieces[piece.Position.X, piece.Position.Y] = null;
                this.squares[target.X, target.Y].Image = (piece as PieceBase).Image;
                this.pieces[target.X, target.Y] = piece;
                ret = true;
            }

            return ret;
        }

        /// <summary>
        /// Gets the figure by coordinates.
        /// </summary>
        /// <param name="x">The x position.</param>
        /// <param name="y">The y position.</param>
        /// <returns>Retuns the piece at the [x, y] coordinates.</returns>
        public IPiece GetFigureByCoords(int x, int y)
        {
            IPiece piece = null;
            
            if (x >= 0 && x < 8 && y >= 0 && y < 8)
            {
                piece = this.Figures[x, y];
            }

            return piece;
        }

        /// <summary>
        /// Gets the figure by position.
        /// </summary>
        /// <param name="position">The position.</param>
        /// <returns>Retuns the piece at the <paramref name="position"/>.</returns>
        public IPiece GetFigureByPosition(Position position)
        {
            return this.GetFigureByCoords(position.X, position.Y);
        }

        /// <summary>
        /// Gets the figure from mouse input.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        /// <returns>Retuns the piece at the current mouse location over the game grid control.</returns>
        public IPiece GetFigureFromMouse(System.Windows.Input.MouseButtonEventArgs e)
        {
            Position mousePosition = this.GetMousePosition(e);
            return this.GetFigureByCoords(mousePosition.X, mousePosition.Y);
        }

        /// <summary>
        /// Gets the mouse position.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        /// <returns><see cref=""/> containing the position square position at the current mouse location.</returns>
        public Position GetMousePosition(System.Windows.Input.MouseButtonEventArgs e)
        {
            Point pt = e.GetPosition(this.gameGrid);
            int cellSizeX = Convert.ToInt32(this.gameGrid.ActualWidth / this.gameGrid.ColumnDefinitions.Count);
            int cellSizeY = Convert.ToInt32(this.gameGrid.ActualHeight / this.gameGrid.RowDefinitions.Count);

            return new Position((int)(pt.X / cellSizeX), (int)(pt.Y / cellSizeY));
        }
    }
}
